/*
 * Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
 *
 * This file is part of SeMoLearning.
 *
 * SeMoLearning is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SeMoLearning is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SeMoLearning.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MEMORY_H_
#define MEMORY_H_

#include "semol/core/SemolExp.h" // DLL export/import defines

#include "utilite/UEventsHandler.h"
#include "semol/core/Parameters.h"
#include "utilite/UVariant.h"
#include <typeinfo>
#include <list>
#include <map>
#include <set>
#include "utilite/UStl.h"
#include <opencv2/core/core.hpp>
#include "semol/core/Sensor.h"
#include "semol/core/Actuator.h"
#include "semol/core/Statistics.h"

class UColorTable;

namespace semol {

class Node;
class NeighborLink;
class GraphNode;
class DBDriver;
class DBTrash;
class SensoryMemory;

class SEMOL_EXP Memory
{
public:
	static const int kIdStart;
	static const int kIdVirtual;
	static const int kIdInvalid;

public:
	Memory(const ParametersMap & parameters = ParametersMap());
	virtual ~Memory();

	virtual void parseParameters(const ParametersMap & parameters);
	bool update(const std::list<SensorRaw> & sensors,
			const std::list<Actuator> & actuators,
			Statistics & stats);
	bool init(const std::string & dbDriverName,
			const std::string & dbUrl,
			bool dbOverwritten = false,
			const ParametersMap & parameters = ParametersMap());
	std::map<int, std::vector<float> > computeLikelihoods(const std::vector<int> & sensors, const std::list<int> & ids);
	float applySensoryAttention(const std::vector<float> & similarities, bool isNormalizedSim = false) const;
	std::map<int, float> applySensoryAttention(const std::map<int, std::vector<float> > & likelihoods, bool isAdjustedLikelihood) const;
	int forget(const std::set<int> & ignoredIds = std::set<int>());
	std::set<int> reactivateNodes(const std::list<int> & ids,
			unsigned int maxLoaded,
			double & timeDbAccess);

	std::vector<float> compareSensors(const std::vector<int> & sensorA, const std::vector<int> & sensorB);
	std::vector<float> compareHistory(
			const std::list<std::vector<int> > & idsA,
			const std::list<std::vector<int> > & idsB);

	int cleanup(const std::list<int> & ignoredIds = std::list<int>());
	void startTrashThread();
	void joinTrashThread();
	bool addLoopClosureLink(int oldId, int newId);
	std::map<int, int> getNeighborsId(int signatureId,
			unsigned int margin,
			int maxCheckedInDatabase = -1,
			bool onlyWithActions = false,
			bool incrementMarginOnLoop = false,
			bool ignoreLoopIds = false,
			double * dbAccessTime = 0) const;

	//getters
	unsigned int getWorkingMemSize() const {return _workingMem.size();}
	unsigned int getStMemSize() const {return _stMem.size();};
	const std::set<int> & getWorkingMem() const {return _workingMem;}
	const std::set<int> & getStMem() const {return _stMem;}
	int getMaxStMemSize() const {return _maxStMemSize;}
	std::list<const NeighborLink *> getNeighborLinks(int signatureId,
			bool ignoreNeighborByLoopClosure = false,
			bool onlyWithActions = false) const;
	void getLoopClosureIds(int signatureId,
			std::set<int> & loopClosureIds,
			std::set<int> & childLoopClosureIds,
			bool lookInDatabase = false) const;
	bool isRawDataKept() const {return _rawDataKept;}
	std::map<int, int> getWeights() const;
	const std::vector<float> & getSensoryAttentionWeights() const {return _sensoryAttentionWeights;}
	const std::vector<float> & getSensoryAttentionSimilarities() const {return _sensoryAttentionSimilarities;}
	const std::list<std::vector<int> > & getHistoryIds() const {return _historyIds;}
	const Node * getLastNode() const;
	const std::vector<int> getLastNodeSensors() const;
	int getDatabaseMemoryUsed() const; // in bytes
	double getDbSavingTime() const;
	virtual std::list<SensorRaw> getRawData(int signatureId) const;
	std::set<int> getAllSignatureIds() const;
	bool memoryChanged() const {return _memoryChanged;}
	const Node * getNode(int id) const;
	bool isInSTM(int signatureId) const {return _stMem.find(signatureId) != _stMem.end();}
	bool isInWM(int signatureId) const {return _workingMem.find(signatureId) != _workingMem.end();}
	bool isInLTM(int signatureId) const {return !this->isInSTM(signatureId) && !this->isInWM(signatureId);}

	//setters
	void setSimilarityMeans(const std::string & rehearsalMeans);
	void setSimilarityStds(const std::string & rehearsalStds);
	void setOldSignatureRatio(float oldSignatureRatio);
	void setMaxStMemSize(unsigned int maxStMemSize);
	void setRecentWmRatio(float recentWmRatio);
	void setSensoryAttentionDecreasingRate(float rate); // percentage / iteration
	void setRawDataKept(bool rawDataKept) {_rawDataKept = rawDataKept;}

	void dumpMemoryTree(const char * fileNameTree) const;
	void dumpMemory(std::string directory) const;
	void dumpSignatures(const char * fileNameSign) const {}
	void generateGraph(const std::string & fileName, std::set<int> ids = std::set<int>());
	void createGraph(GraphNode * parent,
			unsigned int maxDepth,
			const std::set<int> & endIds = std::set<int>());

	void setRoi(const std::string & roi);

	const std::vector<unsigned char> & getMotionMask() const {return _motionMask;}
	const Sensor * getSensor(int sensorId) const;

protected:
	virtual void preUpdate();
	virtual void postUpdate() {}

	virtual void addNodeToSTM(Node * signature,
			const std::list<Actuator> & actuators = std::list<Actuator>());
	virtual void clear();
	virtual void moveToTrash(Node * s);

	void addSignatureToWm(Node * signature);
	Node * _getNode(int id) const;
	std::list<Node *> getRemovableSignatures(int count,
			const std::set<int> & ignoredIds = std::set<int>());
	int getNextId();
	void initCountId();
	bool rehearsal(Node * signature, Statistics & stats);
	bool rehearsalMerge(int oldId, int newId);

	const std::map<int, Node*> & getSignatures() const {return _signatures;}

private:
	Node * createNode(int id,
			const std::list<SensorRaw> & sensors);

	void createVirtualSignature(Node ** signature);
	void updateSensoryAttentionWeights(const std::vector<float> & similarities);
	bool isInNeighborLimits(int id, const std::set<int> & aids) const;

	float getAndUpdateSimilaritySensorMap(const Sensor * sA, const Sensor * sB);
	float compareSensors(const Sensor * sA, const Sensor * sB) const;
	void cleanUnusedSensors();

protected:
	DBDriver * _dbDriver;
	DBTrash * _dbTrash;

private:
	// parameters
	std::vector<float> _similarityMeans;
	std::vector<float> _similarityStds;
	bool _rawDataKept;
	int _maxStMemSize;
	float _recentWmRatio;
	float _sensoryAttentionDecreasingRate;
	bool _stmSizeById;

	//parameters - sensorimotor stuff...
	std::vector<int> _motionPrevious;
	std::vector<unsigned char> _motionMask;
	SensoryMemory * _sensoryMem;

	int _idCount;
	Node * _lastNode;
	std::vector<int> _lastNodeSensors;
	int _lastLoopClosureId;
	bool _memoryChanged; // False by default, become true when Memory::update() is called.
	int _signaturesAdded;

	std::map<int, Node *> _signatures; // TODO : check if a signature is already added? although it is not supposed to occur...
	std::set<int> _stMem; // id
	std::set<int> _workingMem; // id,age
	std::list<std::vector<int> > _historyIds; //  oldest -> newest sensor ids
	std::vector<float> _sensoryAttentionWeights;
	std::vector<float> _sensoryAttentionSimilarities;

};

} // namespace rtabmap

#endif /* MEMORY_H_ */
